package com.gitee.wsl.text.values

import com.gitee.wsl.text.parameter.caseInsensitiveMap
import kotlin.jvm.JvmName

public interface StringValuesBuilder {
    public val caseInsensitiveName: Boolean
    public fun getAll(name: String): List<String>?

    public operator fun contains(name: String): Boolean
    public fun contains(name: String, value: String): Boolean
    public fun names(): Set<String>
    public fun isEmpty(): Boolean
    public fun entries(): Set<Map.Entry<String, List<String>>>

    public operator fun set(name: String, value: String)

    public operator fun get(name: String): String?
    public fun append(name: String, value: String)
    public fun appendAll(stringValues: StringValues)
    public fun appendAll(name: String, values: Iterable<String>)
    public fun appendMissing(stringValues: StringValues)
    public fun appendMissing(name: String, values: Iterable<String>)
    public fun remove(name: String)
    public fun removeKeysWithNoEntries()
    public fun remove(name: String, value: String): Boolean
    public fun clear()

    public fun build(): StringValues
}

/**
 * Append values from [source] filtering values by the specified [predicate]
 *
 * [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.util.appendFiltered)
 *
 * @param keepEmpty when `true` will keep empty lists otherwise keys with no values will be discarded
 */
public fun StringValuesBuilder.appendFiltered(
    source: StringValues,
    keepEmpty: Boolean = false,
    predicate: (String, String) -> Boolean
) {
    source.forEach { name, value ->
        val list = value.filterTo(ArrayList(value.size)) { predicate(name, it) }
        if (keepEmpty || list.isNotEmpty()) {
            appendAll(name, list)
        }
    }
}

/**
 * Append all values from the specified [builder]
 *
 * [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.util.appendAll)
 */
public fun StringValuesBuilder.appendAll(builder: StringValuesBuilder): StringValuesBuilder = apply {
    builder.entries().forEach { (name, values) ->
        appendAll(name, values)
    }
}

/**
 * Appends [name] [value] pair if there are no values associated with [name]
 *
 * [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.util.appendIfNameAbsent)
 */
public fun StringValuesBuilder.appendIfNameAbsent(name: String, value: String): StringValuesBuilder = apply {
    if (contains(name)) return@apply
    append(name, value)
}

/**
 * Appends [name] [value] pair if there is no existing [name] [value] pair
 *
 * [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.util.appendIfNameAndValueAbsent)
 */
public fun StringValuesBuilder.appendIfNameAndValueAbsent(name: String, value: String): StringValuesBuilder = apply {
    if (contains(name, value)) return@apply
    append(name, value)
}

/**
 * Appends multiple key-value pairs to this builder
 *
 * [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.util.appendAll)
 *
 * @param values the key-value pairs to append
 * @return this builder instance
 */
public fun StringValuesBuilder.appendAll(vararg values: Pair<String, String>): StringValuesBuilder = apply {
    values.forEach { (key, value) -> append(key, value) }
}

/**
 * Appends multiple key-value pairs where values are [Iterable] to this builder
 *
 * [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.util.appendAll)
 *
 * @param values the key-value pairs to append where values are [Iterable] of strings
 * @return this builder instance
 */
@JvmName("appendAllIterable")
public fun StringValuesBuilder.appendAll(vararg values: Pair<String, Iterable<String>>): StringValuesBuilder = apply {
    values.forEach { (key, value) -> appendAll(key, value) }
}

/**
 * Appends multiple key-value pairs from a [Map] where values are [Iterable] to this builder
 *
 * [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.util.appendAll)
 *
 * @param values the map containing key-value pairs to append where values are [Iterable] of strings
 * @return this builder instance
 */
@JvmName("appendAllIterable")
public fun StringValuesBuilder.appendAll(values: Map<String, Iterable<String>>): StringValuesBuilder = apply {
    values.forEach { (key, value) -> appendAll(key, value) }
}

/**
 * Appends multiple key-value pairs from a [Map] to this builder
 *
 * [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.util.appendAll)
 *
 * @param values the map containing key-value pairs to append
 * @return this builder instance
 */
public fun StringValuesBuilder.appendAll(values: Map<String, String>): StringValuesBuilder = apply {
    values.forEach { (key, value) -> append(key, value) }
}


@Suppress("KDocMissingDocumentation", "DEPRECATION")
public open class StringValuesBuilderImpl(
    final override val caseInsensitiveName: Boolean = false,
    size: Int = 8
) : StringValuesBuilder {

    protected val values: MutableMap<String, MutableList<String>> =
        if (caseInsensitiveName) caseInsensitiveMap() else LinkedHashMap(size)

    override fun getAll(name: String): List<String>? = values[name]

    override operator fun contains(name: String): Boolean = name in values

    override fun contains(name: String, value: String): Boolean = values[name]?.contains(value) ?: false

    override fun names(): Set<String> = values.keys

    override fun isEmpty(): Boolean = values.isEmpty()

    override fun entries(): Set<Map.Entry<String, List<String>>> = values.entries

    override operator fun set(name: String, value: String) {
        validateValue(value)
        val list = ensureListForKey(name)
        list.clear()
        list.add(value)
    }

    override operator fun get(name: String): String? = getAll(name)?.firstOrNull()

    override fun append(name: String, value: String) {
        validateValue(value)
        ensureListForKey(name).add(value)
    }

    override fun appendAll(stringValues: StringValues) {
        stringValues.forEach { name, values ->
            appendAll(name, values)
        }
    }

    override fun appendMissing(stringValues: StringValues) {
        stringValues.forEach { name, values ->
            appendMissing(name, values)
        }
    }

    override fun appendAll(name: String, values: Iterable<String>) {
        ensureListForKey(name).let { list ->
            values.forEach { validateValue(it) }
            list.addAll(values)
        }
    }

    override fun appendMissing(name: String, values: Iterable<String>) {
        val existing = this.values[name]?.toSet() ?: emptySet()

        appendAll(name, values.filter { it !in existing })
    }

    override fun remove(name: String) {
        values.remove(name)
    }

    override fun removeKeysWithNoEntries() {
        for ((k, _) in values.filter { it.value.isEmpty() }) {
            remove(k)
        }
    }

    override fun remove(name: String, value: String): Boolean = values[name]?.remove(value) ?: false

    override fun clear() {
        values.clear()
    }

    override fun build(): StringValues {
        return StringValuesImpl(caseInsensitiveName, values)
    }

    protected open fun validateName(name: String) {
    }

    protected open fun validateValue(value: String) {
    }

    private fun ensureListForKey(name: String): MutableList<String> {
        return values[name] ?: mutableListOf<String>().also {
            validateName(name)
            values[name] = it
        }
    }
}
